;Copyright (C) 2011 Salil Bhagurkar
;
;This file is part of illusion
;
;illusion is free software: you can redistribute it and/or modify
;it under the terms of the GNU Lesser General Public License as published by
;the Free Software Foundation, either version 3 of the License, or
;(at your option) any later version.
;
;illusion is distributed in the hope that it will be useful,
;but WITHOUT ANY WARRANTY; without even the implied warranty of
;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;GNU Lesser General Public License for more details.
;
;You should have received a copy of the GNU Lesser General Public License
;along with illusion.  If not, see <http://www.gnu.org/licenses/>.

;Contains James Molloy's code, reformatted to suit illusion
;http://www.jamesmolloy.co.uk/
;http://code.google.com/p/jamesm-tutorials/

[BITS 32]
[section .text]
global boot


;This is the multiboot header required by grub loader. it specifies the code,data size

ALIGN 4
mboot:
	MULTIBOOT_PAGE_ALIGN	equ 1<<0
	MULTIBOOT_MEMORY_INFO	equ 1<<1
	MULTIBOOT_AOUT_KLUDGE	equ 1<<16
	MULTIBOOT_HEADER_MAGIC	equ 0x1BADB002
	MULTIBOOT_HEADER_FLAGS	equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
	MULTIBOOT_CHECKSUM	equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
	EXTERN _code, _data, _ehframe, _bss, _end


	dd MULTIBOOT_HEADER_MAGIC
	dd MULTIBOOT_HEADER_FLAGS
	dd MULTIBOOT_CHECKSUM


	dd mboot
	dd _code
	dd _bss
	dd _end
	dd boot

bootprev db "PREV"
boot:
	mov [__grub_multiboot_info], ebx
	mov esp, 0x90000

	extern _kernel_init
	call _kernel_init
	cli
	hlt

extern _sys_exitprocess


global __gdt_flush
__gdt_flush:
	push eax

	mov eax, [esp + 8]  ; Get the pointer to the GDT, passed as a parameter.
	lgdt [eax]        ; Load the new GDT pointer

	mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
	mov ds, ax        ; Load all data segment selectors
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	jmp 0x08:.flush   ; 0x08 is the offset to our code segment: Far jump!
.flush:

	pop eax
	ret

global __idt_flush
__idt_flush:
	push eax

	mov eax, [esp + 8]  ; Get the pointer to the IDT, passed as a parameter.
	lidt [eax]        ; Load the IDT pointer.

	pop eax
	ret
.end:

global __read_cr0
__read_cr0:
	mov eax, cr0
	ret

global __read_cr2
__read_cr2:
	mov eax, cr2
	ret

global __read_cr3
__read_cr3:
	mov eax, cr3
	ret




global __tss_flush
__tss_flush:
	push eax
	mov ax, 0x3B	; Load the index of our TSS structure - The index is
			; 0x28, as it is the 5th selector and each is 8 bytes
			; long, but we set the bottom two bits (making 0x2B)
			; so that it has an RPL of 3, not zero.
	ltr ax		; Load 0x2B into the task state register.
	pop eax
	ret


global __bochs_break
__bochs_break:
	xchg bx, bx
	ret

global __debug_break
__debug_break:
	int 3
	ret

global __wp_enable
__wp_enable:
	mov eax, cr0
	or eax, 0x10000
	mov cr0, eax
	ret

global __switch_to_process_no_save ;(void *stack, void *cr3)
__switch_to_process_no_save:
	mov ebx, [esp + 4]	;stack
	mov ecx, [esp + 8]	;cr3

	;Load the page directory of the target process
	mov cr3, ecx
	;Load the stack
	mov esp, ebx


	;Pop off all context
	popa
	popf

	;Perform a ret to get back to the old process
	ret



global __switch_to_process ;(void **save_stack, void *stack, void *cr3)
__switch_to_process:
	;Push all registers to source process stack
	pushf
	pusha

	;Get the location where we must save the stack
	mov eax, [esp + 40]
	;Store the resultant stack pointer
	mov [eax], esp

	; Load all parameters
	mov ebx, [esp + 40 + 4]		;The stack
	mov ecx, [esp + 40 + 8]		;cr3


	;Load the page directory of the target process
	mov cr3, ecx
	;Load the stack
	mov esp, ebx


	;Pop off all context
	popa
	popf

	;Perform a ret to get back to the old process
	ret


global __switch_to_new_user_process ;(void **save_stack, void *stack, void (*main)(), void *cr3)
__switch_to_new_user_process:
	;Push all registers to source process stack

	pushf
	pusha

	;Get the location where we must save the stack
	mov eax, [esp + 40]
	;Store the resultant stack pointer
	mov [eax], esp

	; Load all parameters
	mov ebx, [esp + 40 + 4]		;The stack
	mov ecx, [esp + 40 + 8]		;Entry
	mov edx, [esp + 40 + 12]	;cr3 (Page directory base)

	;Load all segments
	mov ax, 23h		;Data segment
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	;Load the new page directory
	mov cr3, edx
	;Move to the new stack for the process
	mov esp, ebx

	;Create a stack frame for the iret to work
	push 23h		;Data segment
	push ebx		;Push the stack passed
	pushf			;Flags

	;Set the IF flag to enable interrupts
	pop eax
	or eax, 200h
	push eax

	;Set the IOPL to 0 to not allow port IO
	pop eax
	and eax, 0ffffcfffh
	push eax

	push 1bh
	push ecx ;Push process entry

	iret


global __switch_to_new_kernel_process ;(void **save_stack, void *stack, void (*main)(), void *cr3)
__switch_to_new_kernel_process:
	;Push all registers to source process stack

	pushf
	pusha

	;Get the location where we must save the stack
	mov eax, [esp + 40]
	;Store the resultant stack pointer
	mov [eax], esp

	; Load all parameters
	mov ebx, [esp + 40 + 4]		;The stack
	mov ecx, [esp + 40 + 8]		;Entry
	mov edx, [esp + 40 + 12]	;cr3 (Page directory base)

	;Load the new page directory
	mov cr3, edx
	;Move to the new stack for the process
	mov esp, ebx

	;This raises the exit event
	push _sys_exitprocess
	;Keep interrupts enabled
	sti
	jmp ecx


global __start_new_user_process ;(void *stack, void (*main)(), void *cr3)
__start_new_user_process:
	mov ebx, [esp + 4]	;The stack
	mov ecx, [esp + 8]	;Entry
	mov edx, [esp + 12]	;cr3 (Page directory base)
	;Load all segments
	mov ax, 23h		;Data segment
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	;Load the new page directory
	mov cr3, edx

	;Move to the new stack for the process
	mov esp, ebx

	;Create a stack frame for the iret to work
	push 23h		;Data segment
	push ebx		;Push the stack passed
	pushf			;Flags

	;Set the IF flag to enable interrupts
	pop eax
	or eax, 200h
	push eax

	;Set the IOPL to 0 to not allow port IO
	pop eax
	and eax, 0ffffcfffh
	push eax

	push 1bh
	push ecx ;Process entry
	iret

global __start_new_kernel_process ;(void *stack, void (*main)(), void *cr3)
__start_new_kernel_process:
	mov ebx, [esp + 4]	;The stack
	mov ecx, [esp + 8]	;Entry
	mov edx, [esp + 12]	;cr3 (Page directory base)

	;Load the new page directory
	mov cr3, edx

	;Move to the new stack for the process
	mov esp, ebx

	;Push return function address
	;This raises the exit event
	push _sys_exitprocess

	sti
	;Simply jump to it
	jmp ecx




global __callbios_no_pt
__callbios_no_pt:
	pusha
	call 0x08:0x7F00
	popa
	ret

global __callbios_pt
__callbios_pt:
	pusha
	mov eax, esp
	mov esp, 0x90000
	push eax

	mov eax, cr0
	and eax, 0x7FFFFFFF
	mov cr0, eax		;Disable page translation

	call 0x08:0x7F00

	mov eax, cr0
	or eax, 0x80000000
	mov cr0, eax		;Re-enable page translation

	pop eax
	mov esp, eax
	popa
ret

global __setjmp
__setjmp:
	mov ecx,dword[esp+4]
	mov dword[ecx],ebx
	mov dword[ecx+4],esi
	mov dword[ecx+8],edi
	mov dword[ecx+12],ebp
	pop edx
	mov dword[ecx+16],esp
	mov dword[ecx+20],edx
	xor eax,eax
	jmp edx

global __longjmp
__longjmp:
	mov eax,dword[esp+8]
	mov ecx,dword[esp+4]
	mov ebx,dword[ecx]
	mov esi,dword[ecx+4]
	mov edi,dword[ecx+8]
	mov ebp,dword[ecx+12]
	mov esp,dword[ecx+16]
	or eax,eax
	jnz dr
	inc eax
	dr:
	jmp dword[ecx+20]


%macro ISR_NOERRCODE 1
global __isr%1
__isr%1:
	cli                         ; Disable interrupts firstly.
	push 0                      ; Push a dummy error code.
	push %1                     ; Push the interrupt number.
	jmp isr_common_stub         ; Go to our common handler code.
%endmacro


%macro ISR_ERRCODE 1
global __isr%1
__isr%1:
	cli                         ; Disable interrupts.
	push %1                     ; Push the interrupt number
	jmp isr_common_stub
%endmacro

%macro IRQ 1
global __irq%1
__irq%1:
	cli
	push byte 0
	push byte %1 ;Interrupt number
	jmp irq_common_stub
%endmacro

ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
ISR_NOERRCODE 4
ISR_NOERRCODE 5
ISR_NOERRCODE 6
ISR_NOERRCODE 7
ISR_ERRCODE   8
ISR_NOERRCODE 9
ISR_ERRCODE   10
ISR_ERRCODE   11
ISR_ERRCODE   12
ISR_ERRCODE   13
ISR_ERRCODE   14
ISR_NOERRCODE 15
ISR_NOERRCODE 16
ISR_NOERRCODE 17
ISR_NOERRCODE 18
ISR_NOERRCODE 19
ISR_NOERRCODE 20
ISR_NOERRCODE 21
ISR_NOERRCODE 22
ISR_NOERRCODE 23
ISR_NOERRCODE 24
ISR_NOERRCODE 25
ISR_NOERRCODE 26
ISR_NOERRCODE 27
ISR_NOERRCODE 28
ISR_NOERRCODE 29
ISR_NOERRCODE 30
ISR_NOERRCODE 31
ISR_NOERRCODE 32
ISR_NOERRCODE 33
ISR_NOERRCODE 34
ISR_NOERRCODE 35
ISR_NOERRCODE 36
ISR_NOERRCODE 37
ISR_NOERRCODE 38
ISR_NOERRCODE 39
ISR_NOERRCODE 40
ISR_NOERRCODE 41
ISR_NOERRCODE 42
ISR_NOERRCODE 43
ISR_NOERRCODE 44
ISR_NOERRCODE 45
ISR_NOERRCODE 46
ISR_NOERRCODE 47
ISR_NOERRCODE 48
ISR_NOERRCODE 49
ISR_NOERRCODE 50
ISR_NOERRCODE 51
ISR_NOERRCODE 52
ISR_NOERRCODE 53
ISR_NOERRCODE 54
ISR_NOERRCODE 55
ISR_NOERRCODE 56
ISR_NOERRCODE 57
ISR_NOERRCODE 58
ISR_NOERRCODE 59
ISR_NOERRCODE 60
ISR_NOERRCODE 61
ISR_NOERRCODE 62
ISR_NOERRCODE 63
ISR_NOERRCODE 64
ISR_NOERRCODE 65
ISR_NOERRCODE 66
ISR_NOERRCODE 67
ISR_NOERRCODE 68
ISR_NOERRCODE 69
ISR_NOERRCODE 70
ISR_NOERRCODE 71
ISR_NOERRCODE 72
ISR_NOERRCODE 73
ISR_NOERRCODE 74
ISR_NOERRCODE 75
ISR_NOERRCODE 76
ISR_NOERRCODE 77
ISR_NOERRCODE 78
ISR_NOERRCODE 79
ISR_NOERRCODE 80
ISR_NOERRCODE 81
ISR_NOERRCODE 82
ISR_NOERRCODE 83
ISR_NOERRCODE 84
ISR_NOERRCODE 85
ISR_NOERRCODE 86
ISR_NOERRCODE 87
ISR_NOERRCODE 88
ISR_NOERRCODE 89
ISR_NOERRCODE 90
ISR_NOERRCODE 91
ISR_NOERRCODE 92
ISR_NOERRCODE 93
ISR_NOERRCODE 94
ISR_NOERRCODE 95
ISR_NOERRCODE 96
ISR_NOERRCODE 97
ISR_NOERRCODE 98
ISR_NOERRCODE 99
ISR_NOERRCODE 100
ISR_NOERRCODE 101
ISR_NOERRCODE 102
ISR_NOERRCODE 103
ISR_NOERRCODE 104
ISR_NOERRCODE 105
ISR_NOERRCODE 106
ISR_NOERRCODE 107
ISR_NOERRCODE 108
ISR_NOERRCODE 109
ISR_NOERRCODE 110
ISR_NOERRCODE 111
ISR_NOERRCODE 112
ISR_NOERRCODE 113
ISR_NOERRCODE 114
ISR_NOERRCODE 115
ISR_NOERRCODE 116
ISR_NOERRCODE 117
ISR_NOERRCODE 118
ISR_NOERRCODE 119
ISR_NOERRCODE 120
ISR_NOERRCODE 121
ISR_NOERRCODE 122
ISR_NOERRCODE 123
ISR_NOERRCODE 124
ISR_NOERRCODE 125
ISR_NOERRCODE 126
ISR_NOERRCODE 127
;ISR_SYSCALL 128
ISR_NOERRCODE 129
ISR_NOERRCODE 130
ISR_NOERRCODE 131
ISR_NOERRCODE 132
ISR_NOERRCODE 133
ISR_NOERRCODE 134
ISR_NOERRCODE 135
ISR_NOERRCODE 136
ISR_NOERRCODE 137
ISR_NOERRCODE 138
ISR_NOERRCODE 139
ISR_NOERRCODE 140
ISR_NOERRCODE 141
ISR_NOERRCODE 142
ISR_NOERRCODE 143
ISR_NOERRCODE 144
ISR_NOERRCODE 145
ISR_NOERRCODE 146
ISR_NOERRCODE 147
ISR_NOERRCODE 148
ISR_NOERRCODE 149
ISR_NOERRCODE 150
ISR_NOERRCODE 151
ISR_NOERRCODE 152
ISR_NOERRCODE 153
ISR_NOERRCODE 154
ISR_NOERRCODE 155
ISR_NOERRCODE 156
ISR_NOERRCODE 157
ISR_NOERRCODE 158
ISR_NOERRCODE 159
ISR_NOERRCODE 160
ISR_NOERRCODE 161
ISR_NOERRCODE 162
ISR_NOERRCODE 163
ISR_NOERRCODE 164
ISR_NOERRCODE 165
ISR_NOERRCODE 166
ISR_NOERRCODE 167
ISR_NOERRCODE 168
ISR_NOERRCODE 169
ISR_NOERRCODE 170
ISR_NOERRCODE 171
ISR_NOERRCODE 172
ISR_NOERRCODE 173
ISR_NOERRCODE 174
ISR_NOERRCODE 175
ISR_NOERRCODE 176
ISR_NOERRCODE 177
ISR_NOERRCODE 178
ISR_NOERRCODE 179
ISR_NOERRCODE 180
ISR_NOERRCODE 181
ISR_NOERRCODE 182
ISR_NOERRCODE 183
ISR_NOERRCODE 184
ISR_NOERRCODE 185
ISR_NOERRCODE 186
ISR_NOERRCODE 187
ISR_NOERRCODE 188
ISR_NOERRCODE 189
ISR_NOERRCODE 190
ISR_NOERRCODE 191
ISR_NOERRCODE 192
ISR_NOERRCODE 193
ISR_NOERRCODE 194
ISR_NOERRCODE 195
ISR_NOERRCODE 196
ISR_NOERRCODE 197
ISR_NOERRCODE 198
ISR_NOERRCODE 199
ISR_NOERRCODE 200
ISR_NOERRCODE 201
ISR_NOERRCODE 202
ISR_NOERRCODE 203
ISR_NOERRCODE 204
ISR_NOERRCODE 205
ISR_NOERRCODE 206
ISR_NOERRCODE 207
ISR_NOERRCODE 208
ISR_NOERRCODE 209
ISR_NOERRCODE 210
ISR_NOERRCODE 211
ISR_NOERRCODE 212
ISR_NOERRCODE 213
ISR_NOERRCODE 214
ISR_NOERRCODE 215
ISR_NOERRCODE 216
ISR_NOERRCODE 217
ISR_NOERRCODE 218
ISR_NOERRCODE 219
ISR_NOERRCODE 220
ISR_NOERRCODE 221
ISR_NOERRCODE 222
ISR_NOERRCODE 223
ISR_NOERRCODE 224
ISR_NOERRCODE 225
ISR_NOERRCODE 226
ISR_NOERRCODE 227
ISR_NOERRCODE 228
ISR_NOERRCODE 229
ISR_NOERRCODE 230
ISR_NOERRCODE 231
ISR_NOERRCODE 232
ISR_NOERRCODE 233
ISR_NOERRCODE 234
ISR_NOERRCODE 235
ISR_NOERRCODE 236
ISR_NOERRCODE 237
ISR_NOERRCODE 238
ISR_NOERRCODE 239
ISR_NOERRCODE 240
ISR_NOERRCODE 241
ISR_NOERRCODE 242
ISR_NOERRCODE 243
ISR_NOERRCODE 244
ISR_NOERRCODE 245
ISR_NOERRCODE 246
ISR_NOERRCODE 247
ISR_NOERRCODE 248
ISR_NOERRCODE 249
ISR_NOERRCODE 250
ISR_NOERRCODE 251
ISR_NOERRCODE 252
ISR_NOERRCODE 253
ISR_NOERRCODE 254
ISR_NOERRCODE 255
IRQ   0
IRQ   1
IRQ   2
IRQ   3
IRQ   4
IRQ   5
IRQ   6
IRQ   7
IRQ   8
IRQ   9
IRQ  10
IRQ  11
IRQ  12
IRQ  13
IRQ  14
IRQ  15

extern _idt_handler
extern _syscall_handler
extern _irq_handler

; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
isr_common_stub:
	pusha                    ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax

	xor eax, eax
	mov ax, ds               ; Lower 16-bits of eax = ds.
	push eax                 ; Save the data segment descriptor

	mov ax, 0x10             ; Load the kernel data segment descriptor
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	push esp    	     ; Push a pointer to the current top of stack - this becomes the registers_t* parameter.
	call _idt_handler         ; Call into our C code.
	add esp, 4		     ; Remove the registers_t* parameter.

	pop ebx                  ; Reload the original data segment descriptor
	mov ds, bx
	mov es, bx
	mov fs, bx
	mov gs, bx

	popa                     ; Pops edi,esi,ebp...
	add esp, 8               ; Cleans up the pushed error code and pushed ISR number
	iret                     ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
.end:

global __isr128
__isr128:
	push eax
	push ecx
	push edx
	push ebx
	push esp
	push ebp
	push esi
	push edi

	xor eax, eax
	mov ax, ds               ; Lower 16-bits of eax = ds.
	push eax                 ; Save the data segment descriptor

	mov ax, 0x10             ; Load the kernel data segment descriptor
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	push esp    	     ; Push a pointer to the current top of stack - this becomes the registers_t* parameter.
	call _syscall_handler         ; Call into our C code.
	add esp, 4		     ; Remove the registers_t* parameter.

	pop ebx                  ; Reload the original data segment descriptor
	mov ds, bx
	mov es, bx
	mov fs, bx
	mov gs, bx


	pop edi
	pop esi
	pop ebp
	pop esp
	pop ebx
	pop edx
	pop ecx
	add esp, 4		; Leave eax the same as returned

	iret                     ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
.end:



; This is our common IRQ stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
irq_common_stub:
	pusha                    ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax

	mov ax, ds               ; Lower 16-bits of eax = ds.
	push eax                 ; Save the data segment descriptor

	mov ax, 0x10             ; Load the kernel data segment descriptor
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	push esp    	     ; Push a pointer to the current top of stack - this becomes the registers_t* parameter.
	call _irq_handler         ; Call into our C code.
	add esp, 4		     ; Remove the registers_t* parameter.

	pop ebx                  ; Reload the original data segment descriptor
	mov ds, bx
	mov es, bx
	mov fs, bx
	mov gs, bx

	popa                     ; Pops edi,esi,ebp...
	add esp, 8               ; Cleans up the pushed error code and pushed ISR number
	iret                     ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
.end:



[section .data]
global __kernel_entry
__kernel_entry dd boot

global __kernel_end
__kernel_end dd _end

global __kernel_data
__kernel_data dd _data

global __kernel_start
__kernel_start dd _code

global __kernel_bss
__kernel_bss dd _bss

global __grub_multiboot_info
__grub_multiboot_info dd 0
